import { Service } from 'egg';
import { v4 as uuidv4 } from 'uuid';
import { col, Transaction, Op } from 'sequelize';

import { checkOption, needOtherModelCheck } from '../../constants/user-basic-info';

class QuestionnaireService extends Service {
  async findQuestionnaireByUuid(uuid: string) {
    const foundQuestionnaires = await this.ctx.model.Questionnaire.findOne({
      where: { uuid }
    });

    return foundQuestionnaires;
  }

  async findQuestionnaireByTemplateUuid(uuid: string) {
    const foundTemplate = await this.ctx.model.Template.findOne({
      include: [
        {
          model: this.ctx.model.Questionnaire,
        },
      ],
      where: { uuid }
    });

    return foundTemplate?.Questionnaires;
  }

  async addQuestionnaire(uuid: string, name: string) {
    const result = await this.ctx.model.transaction(async (t) => {
      const template = await this.ctx.model.Template.findOne({
        where: { uuid },
        attributes: [['id', 'templateId']],
        raw: true
      }, { transaction: t });

      if (!template) {
        return null;
      }

      const { templateId } = template;

      const isExistName = await this.ctx.model.Questionnaire.findOne({
        where: {
          name,
          fk_template: templateId
        }
      }, { transaction: t });

      if (isExistName === null) {
        const questionnaireUuid = uuidv4();
        const newQuestionnaire = await this.ctx.model.Questionnaire.create(
          {
            uuid: questionnaireUuid,
            name,
            FKTemplate: templateId,
          }, { transaction: t }
        );
        return newQuestionnaire;
      }
      return null;
    });

    return result;
  }

  async setOptionVisible(
    uuid: string,
    basicInformationName: string,
    value: number
  ) {
    if (value === 1 && needOtherModelCheck(basicInformationName)) {
      // 开启时判断对应的optionlist是否是空
      const { model, alias } = checkOption(basicInformationName);
      const optionCount = await this.ctx.model.Questionnaire.count({
        include: [
          {
            model: this.ctx.model[model],
            attributes: ['uuid', 'name'],
            required: true,
            as: alias
          },
        ],
        where: { uuid },
      });
      if (optionCount === 0) {
        return false;
      }
    }

    const optionVisible = await this.ctx.model.Questionnaire.update(
      {
        [basicInformationName]: value
      },
      { where: { uuid } }
    );

    return optionVisible;
  }

  async deleteQuestionnaire(
    uuid: string,
  ) {
    try {
      const result = await this.ctx.model.transaction(async (t) => {
        // 查四个visable，然后根据visable判断删除选项数据
        const visableList = await this.ctx.model.Questionnaire.findOne({
          attributes: [
            'id',
          ],
          where: { uuid }
        }, { transaction: t });
        const {
          id,
        } = visableList;
        await Promise.all([
          this.ctx.model.WorkUnit.destroy(
            { where: { FKQuestionnaire: id } }, { transaction: t }
          ),
          this.ctx.model.AffiliatedUnit.destroy(
            { where: { FKQuestionnaire: id } }, { transaction: t }
          ),
          this.ctx.model.JobPosition.destroy(
            { where: { FKQuestionnaire: id } }, { transaction: t }
          ),
          this.ctx.model.OperatingPost.destroy(
            { where: { FKQuestionnaire: id } }, { transaction: t }
          ),
        ]);

        const removedQuestionnaire = await this.ctx.model.Questionnaire.destroy(
          { where: { uuid } }, { transaction: t }
        );
        return removedQuestionnaire;
      });
      return result;
    } catch (error) {
      console.error(error);
      return undefined;
    }
  }

  async switchHideSettings(uuid: string, open: number) {
    const changedSwitch = await this.ctx.model.Questionnaire.update({
      open
    }, { where: { uuid } });

    return changedSwitch;
  }

  async updateQuestionnaire(uuid: string, name: string) {
    const { ctx } = this;
    const result = await this.ctx.model.transaction(async (t) => {
      const questionnaire = await ctx.model.Questionnaire.findOne({
        where: { uuid },
        attributes: [['fk_template', 'templateId']],
        raw: true
      }, { transaction: t });

      if (!questionnaire) {
        return undefined;
      }

      const { templateId } = questionnaire;

      const alreadyExistName = await ctx.model.Questionnaire.findOne({
        where: {
          fk_template: templateId,
          name,
        }
      }, { transaction: t });

      if (alreadyExistName) {
        return undefined;
      }

      const changedQuestionnaires = await ctx.model.Questionnaire.update({
        name,
      }, {
        where: { uuid }
      }, { transaction: t });

      return changedQuestionnaires;
    });
    return result;
  }

  async findTemplateByQuestionnaireUuid(uuid: string) {
    const template = await this.ctx.model.Template.findOne({
      include: [
        {
          model: this.ctx.model.Questionnaire,
          where: { uuid },
          required: false
        },
      ],
      raw: true
    });

    return template;
  }

  async findQuesionList(t: Transaction) {
    const questionList = await this.ctx.model.Question.findAll(
      {
        attributes: ['id', 'uuid', 'name'],
        order: [['id', 'ASC']]
      },
      { transaction: t }
    );
    return questionList;
  }

  async findQuestionnaireReferenceDetail(uuid: string, t?: Transaction) {
    const detail = await this.ctx.model.Questionnaire.findOne({
      include: [
        {
          model: this.ctx.model.Template,
          attributes: []
        },
        {
          model: this.ctx.model.OperatingPost,
          attributes: ['uuid', 'name'],
          as: 'operatingPost'
        },
        {
          model: this.ctx.model.JobPosition,
          attributes: ['uuid', 'name'],
          as: 'jobPosition'
        },
        {
          model: this.ctx.model.AffiliatedUnit,
          attributes: ['uuid', 'name'],
          as: 'affiliatedUnit'
        },
        {
          model: this.ctx.model.WorkUnit,
          attributes: ['uuid', 'name'],
          as: 'workUnit'
        },
      ],
      attributes: [
        ['uuid', 'questionnaireUuid'],
        'open',
        ['name', 'subTitle'],
        'userNameVisible',
        'userNumVisible',
        'jobPositionVisible',
        'affiliatedVisible',
        'workUnitVisible',
        'operatingPostVisible',
        [col('Template.title'), 'title'],
        [col('Template.description'), 'description'],
        [col('Template.epilog'), 'epilog'],
        [col('Template.logo'), 'logo'],
      ],
      where: { uuid },
    }, { transaction: t });
    return detail;
  }

  async searchDetail(uuid: string) {
    try {
      const result = await this.ctx.model.transaction(async (t) => {
        const [
          questionList,
          questionnaireDetail
        ] = await Promise.all([
          this.findQuesionList(t),
          this.findQuestionnaireReferenceDetail(uuid, t)
        ]);
        const questionnaireDetailDataValue = questionnaireDetail.toJSON();
        questionnaireDetailDataValue.questionList = questionList;

        return questionnaireDetailDataValue;
      });
      return result;
    } catch (error) {
      console.error(error);
      return undefined;
    }
  }

  async findQuestionnaireOption(
    templateUuid: string,
    name: string,
    isAttachAll?: boolean
  ) {
    const { ctx } = this;

    if (templateUuid === 'all') {
      return [{
        uuid: 'all',
        name: '全部'
      }];
    }

    const foundQuestionnaires = await ctx
      .model
      .Questionnaire
      .findAll({
        where: { name: { [Op.like]: `%${name}%` } },
        attributes: ['uuid', 'name'],
        include: [{
          model: this.ctx.model.Template,
          attributes: [],
          where: {
            uuid: templateUuid
          }
        }],
      });

    if (isAttachAll) {
      foundQuestionnaires.unshift({
        uuid: 'all',
        name: '全部'
      });
    }

    return foundQuestionnaires;
  }

  async findMergeByUuid(questionnaireUuid: string) {
    const { ctx } = this;

    const result = await ctx.model.transaction(async (t) => {
      const questionnaire = await ctx.model.Questionnaire.findOne({
        where: { uuid: questionnaireUuid },
        attributes: ['id'],
      }, { transaction: t });

      if (!questionnaire) {
        return false;
      }

      const { id } = questionnaire;

      const merge = await ctx.model.MergeUserQuestionnaire.count({
        where: { fk_questionnaire: id }
      }, { transaction: t });

      return merge === 0;
    });
    return result;
  }
}

export default QuestionnaireService;
